package org.sk.gsync.app.synch;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;

import com.google.common.collect.Lists;
import com.google.gdata.client.DocumentQuery;
import com.google.gdata.client.Query.CustomParameter;
import com.google.gdata.client.Service.GDataRequest;
import com.google.gdata.client.Service.GDataRequest.RequestType;
import com.google.gdata.client.docs.DocsService;
import com.google.gdata.client.media.ResumableGDataFileUploader;
import com.google.gdata.data.ExtensionProfile;
import com.google.gdata.data.Link;
import com.google.gdata.data.MediaContent;
import com.google.gdata.data.ParseSource;
import com.google.gdata.data.PlainTextConstruct;
import com.google.gdata.data.docs.DocumentEntry;
import com.google.gdata.data.docs.DocumentListEntry;
import com.google.gdata.data.docs.DocumentListFeed;
import com.google.gdata.data.docs.FolderEntry;
import com.google.gdata.data.docs.PdfEntry;
import com.google.gdata.data.docs.PresentationEntry;
import com.google.gdata.data.docs.RevisionEntry;
import com.google.gdata.data.docs.RevisionFeed;
import com.google.gdata.data.docs.SpreadsheetEntry;
import com.google.gdata.data.media.MediaFileSource;
import com.google.gdata.util.ServiceException;

public class GoogleDocsHelper {

    private static final Logger LOGGER = Logger
        .getLogger(GoogleDocsHelper.class);

    @Autowired
    protected DocsService docsService;

    private final String BASE_URL =
        "https://docs.google.com/feeds/default/private?v=3";

    private final String FEED_URL =
        "https://docs.google.com/feeds/default/private/full?v=3";

    private static final String RESUMABLE_UPLOAD_URL =
        "https://docs.google.com/feeds/upload/create-session/default/private/full";

    private final String FOLDER_URL =
        "https://docs.google.com/feeds/default/private/full/-/folder?showfolders=true";

    /**
     * Creates a folder in Google Docs server.
     * 
     * @param title
     *            The document title.
     * @return An {@link DefaultSynchItem} object with resource ID.
     * @throws IOException
     * @throws ServiceException
     */
    public DefaultSynchItem createFolder(final String title)
            throws IOException, ServiceException {
        DocumentListEntry newEntry = new FolderEntry();
        newEntry.setTitle(new PlainTextConstruct(title));
        URL feedUrl = new URL(FEED_URL);
        FolderSynch item = null;
        DocumentListEntry entry = getEntryByTitle(title);
        if (entry == null) {
            newEntry = docsService.insert(feedUrl, newEntry);
            LOGGER.info("Folder " + title
                + " has been created on Google Docs.");
            item = new FolderSynch();
        }
        return item;
    }

    public void deleteFolder(final String title)
            throws IOException, ServiceException {
        DocumentListEntry entry = getFolder(title);
        if (entry != null) {
            Link editLink = entry.getEditLink();
            docsService.delete(new URL(editLink.getHref()),
                entry.getEtag());
            LOGGER.info("Folder " + title
                + " has been deleted on Google Docs.");
        }
    }

    public DocumentListFeed retrieveFolderContent(final String titleFolder)
            throws IOException, ServiceException {
        FolderEntry folderEntry =
            (FolderEntry) getEntryByTitle(titleFolder);
        String foldersFeedUri =
            ((MediaContent) folderEntry.getContent()).getUri();
        DocumentQuery query = new DocumentQuery(new URL(foldersFeedUri));
        return docsService.getFeed(query, DocumentListFeed.class);
    }

    /**
     * public DocumentListEntry uploadFile(String filepath, String title, URL
     * uri)
     * 
     * @throws ServiceException
     * @throws IOException
     * @throws InterruptedException
     */
    // public void uploadFile(final String folder, final File file)
    // throws IOException, ServiceException {
    // String destFolderUrl = FEED_URL;
    // if (folder != null) {
    // FolderEntry folderEntry =
    // (FolderEntry) getEntryByTitle(folder);
    // destFolderUrl =
    // ((MediaContent) folderEntry.getContent()).getUri();
    // }
    // DocumentListEntry newDocument = new DocumentListEntry();
    // String mimeType = null;
    // try {
    // if (DocumentListEntry.MediaType.valueOf(file.getName()) != null) {
    // MediaType mediaType =
    // DocumentListEntry.MediaType.fromFileName(file
    // .getName());
    // mimeType = mediaType.getMimeType();
    // }
    // } catch (IllegalArgumentException e) {
    // mimeType = "application/octet-stream";
    // }
    // newDocument.setFile(file, mimeType);
    // newDocument.setTitle(new PlainTextConstruct(file.getName()));
    // docsService.insert(new URL(destFolderUrl), newDocument);
    // }

    public void uploadFile(final String folder, final File file)
            throws IOException, ServiceException, InterruptedException {
        int MAX_CONCURRENT_UPLOADS = 10;
        int PROGRESS_UPDATE_INTERVAL = 1000;
        int DEFAULT_CHUNK_SIZE = 10485760;

        // Create a listener
        FileUploadProgressListener listener =
            new FileUploadProgressListener(); // See the sample for details on
                                              // this class.

        // Pool for handling concurrent upload tasks
        ExecutorService executor =
            Executors.newFixedThreadPool(MAX_CONCURRENT_UPLOADS);

        // Create {@link ResumableGDataFileUploader} for each file to upload
        List<ResumableGDataFileUploader> uploaders = Lists.newArrayList();

        String contentType;
        try {
            contentType =
                DocumentListEntry.MediaType.fromFileName(file.getName())
                    .getMimeType();
        } catch (IllegalArgumentException e) {
            contentType = "application/octet-stream";
        }
        MediaFileSource mediaFile = new MediaFileSource(file, contentType);
        ResumableGDataFileUploader uploader =
            new ResumableGDataFileUploader.Builder(docsService,
                getResumableURL(folder), mediaFile, null)
                .title(file.getName()).chunkSize(DEFAULT_CHUNK_SIZE)
                .executor(executor)
                .trackProgress(listener, PROGRESS_UPDATE_INTERVAL).build();
        uploaders.add(uploader);

        listener.listenTo(uploaders); // attach the listener to list of
                                      // uploaders

        // Start the upload(s)
        // for (ResumableGDataFileUploader uploader : uploaders) {
        uploader.start();
        // }

        // wait for uploads to complete
        while (!listener.isDone()) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException ie) {
                listener.printResults();
                throw ie; // rethrow
            }
        }
    }

    public void deleteFile(final File file)
            throws IOException, ServiceException {
        DocumentListEntry entry = getEntryByTitle(file.getName());
        if (entry != null) {
            String uri = ((MediaContent) entry.getContent()).getUri();
            docsService.delete(new URL(uri));
        }
    }

    // public void deleteEntryOnServer(final String resourceID)
    // throws IOException, ServiceException {
    // if (existsEntryOnServer(resourceID)) {
    // URL entryUrl = new URL(FEED_URL + resourceID);
    // docsService.delete(entryUrl);
    // LOGGER.info("Deleted entry " + resourceID);
    // }
    // }

    // public DocumentListEntry getEntry(final String title)
    // throws IOException, ServiceException {
    // URL feedUri = new URL(FEED_URL);
    // DocumentQuery query = new DocumentQuery(feedUri);
    // query.setSortMode("title");
    // query.setTitleQuery(title);
    // query.setTitleExact(true);
    // query
    // .addCustomParameter(new CustomParameter("showfolders", "true"));
    // query.setMaxResults(10);
    // DocumentListFeed feed =
    // docsService.getFeed(query, DocumentListFeed.class);
    // List<DocumentListEntry> entries = feed.getEntries();
    // if (entries.isEmpty()) {
    // return null;
    // }
    // return entries.get(0);
    // }

    public List<DocumentListEntry> getAllEntriesFromServer()
            throws IOException, ServiceException {
        URL feedUri = new URL(FEED_URL);
        DocumentQuery query = new DocumentQuery(feedUri);
        query
            .addCustomParameter(new CustomParameter("showfolders", "true"));
        DocumentListFeed feed =
            docsService.getFeed(query, DocumentListFeed.class);
        return feed.getEntries();
    }

    public List<DocumentListEntry> getFolders()
            throws IOException, ServiceException {
        URL feedUri = new URL(FOLDER_URL);
        DocumentQuery query = new DocumentQuery(feedUri);
        DocumentListFeed feed =
            docsService.getFeed(query, DocumentListFeed.class);
        return feed.getEntries();
    }

    public DocumentListEntry getFolder(final String title)
            throws IOException, ServiceException {
        URL feedUri = new URL(FEED_URL);
        DocumentQuery query = new DocumentQuery(feedUri);
        query.setTitleQuery(title);
        query.setTitleExact(true);
        query
            .addCustomParameter(new CustomParameter("showfolders", "true"));
        DocumentListFeed feed =
            docsService.getFeed(query, DocumentListFeed.class);
        List<DocumentListEntry> entries = feed.getEntries();
        if (entries.isEmpty()) {
            return null;
        }
        return entries.get(0);
    }

    public DocumentListEntry getEntryByTitle(final String title)
            throws IOException, ServiceException {
        LOGGER.debug("Looking for " + title
            + " file in Google Docs Server");
        DocumentListEntry entry = null;
        DocumentQuery query = new DocumentQuery(new URL(FEED_URL));
        query.setTitleQuery(title);
        query.setTitleExact(true);
        query.setMaxResults(1);
        query
            .addCustomParameter(new CustomParameter("showfolders", "true"));
        DocumentListFeed feed =
            docsService.getFeed(query, DocumentListFeed.class);
        List<DocumentListEntry> entries = feed.getEntries();
        if (entries.size() > 0) {
            entry = entries.get(0);
        }
        return entry;
    }

    public DocumentListEntry getEntryByID(final String resourceID)
            throws IOException, ServiceException {
        LOGGER.debug("Looking for resource with id " + resourceID
            + " in Google Docs Server");
        DocumentListEntry entry = null;
        DocumentQuery query = new DocumentQuery(new URL(FEED_URL));
        query
            .addCustomParameter(new CustomParameter("resourceID", "true"));
        query.setMaxResults(1);
        query
            .addCustomParameter(new CustomParameter("showfolders", "true"));
        DocumentListFeed feed =
            docsService.getFeed(query, DocumentListFeed.class);
        List<DocumentListEntry> entries = feed.getEntries();
        if (entries.size() > 0) {
            entry = entries.get(0);
        }
        return entry;
    }

    public DocumentListEntry moveToFolder(
            final DocumentListEntry sourceEntry,
            final DocumentListEntry destFolderEntry)
            throws IOException, MalformedURLException, ServiceException {
        DocumentListEntry newEntry = null;
        String docType = sourceEntry.getType();
        if (docType.equals("document")) {
            newEntry = new DocumentEntry();
        } else if (docType.equals("presentation")) {
            newEntry = new PresentationEntry();
        } else if (docType.equals("spreadsheet")) {
            newEntry = new SpreadsheetEntry();
        } else if (docType.equals("folder")) {
            newEntry = new FolderEntry();
        } else if (docType.equals("pdf")) {
            newEntry = new PdfEntry();
        } else {
            newEntry = new DocumentListEntry(); // Unknown type
        }
        newEntry.setId(sourceEntry.getId());
        String destFolderUri =
            ((MediaContent) destFolderEntry.getContent()).getUri();
        return docsService.insert(new URL(destFolderUri), newEntry);
    }

    public List<DocumentListEntry> executeURL(final URL url)
            throws IOException, ServiceException {
        GDataRequest createRequest =
            docsService.createRequest(RequestType.QUERY, url, null);
        createRequest.execute();
        ParseSource parseSource = createRequest.getParseSource();
        InputStream inputStream = parseSource.getInputStream();
        DocumentListFeed feed = new DocumentListFeed();
        feed.parseAtom(new ExtensionProfile(), inputStream);
        return feed.getEntries();
    }

    public List<RevisionEntry> getRevisions(final DocumentListEntry entry)
            throws IOException, ServiceException {
        RevisionFeed feed =
            docsService.getFeed(new URL(entry.getSelfLink().getHref()
                + "/revisions"), RevisionFeed.class);
        return feed.getEntries();
    }

    public List<DocumentListEntry> getGDocsChanges(final String resourceID)
            throws IOException, ServiceException {
        StringBuffer sb = new StringBuffer();
        sb.append(BASE_URL);
        sb.append("/changes");
        // sb.append("/" + resourceID);
        URL url = new URL(sb.toString());
        return executeURL(url);
    }

    public URL getResumableURL(final String folder)
            throws MalformedURLException {
        StringBuffer sb = new StringBuffer();
        sb.append(RESUMABLE_UPLOAD_URL);
        if (folder != null) {
            sb.append("/");
            sb.append(folder);
        }
        return new URL(sb.toString());
    }

}
